import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:ideology/shared/widgets/code_input/core/text_buffer.dart';
import 'package:ideology/shared/widgets/code_input/highlight/highlight_theme.dart';
import 'package:ideology/shared/widgets/code_input/highlight/syntax_highlighter.dart';


// === 光标外观可调常量 ===
const double kCaretWidth = 2.5;        // 光标宽度
const double kCaretPadding = 1.0;      // 光标上下留白
const Radius kCaretRadius = Radius.circular(1.0);

// === 滚动条样式常量 ===
const double kScrollbarWidth = 6.0;
const double kScrollbarMargin = 4.0;
const double kScrollbarMinThumb = 24.0;
const Radius kScrollbarRadius = Radius.circular(3.0);

/// **带行号与滚动条的编辑器绘制器**。
///
/// - 绘制内容包括：
///   - 背景（整体、行号栏）；
///   - 行号（当前行高亮蓝色）；
///   - 文本（带语法高亮 spans）；
///   - 选区背景；
///   - 光标；
///   - 可选的右侧滚动条。
///
/// - 内部使用 [TextBuffer] 提供的行文本，
///   [SyntaxHighlighter] 生成的语法高亮 token，
///   [HighlightTheme] 提供样式。
///
/// - 通过行缓存 `_LineCacheEntry` 避免重复 `TextPainter.layout`，提升性能。
class LineNumberEditorPainter extends CustomPainter {
  final TextBuffer buffer;           // 文本存储
  final int cursorPos;               // 光标位置（绝对索引）
  final bool showCursor;             // 是否显示光标
  final int? selectionStart;         // 选区起点（绝对索引）
  final int? selectionEnd;           // 选区终点（绝对索引）
  final double scrollY;              // 垂直滚动偏移量

  /// 布局参数
  final double lineHeight;           // 行高
  final double padding;              // 内边距
  final double lineNumberWidth;      // 行号栏宽度

  final SyntaxHighlighter highlighter; // 语法高亮器
  final HighlightTheme theme;          // 样式主题

  /// 是否绘制右侧滚动条
  final bool showScrollbar;

  const LineNumberEditorPainter({
    required this.scrollY,
    required this.buffer,
    required this.cursorPos,
    required this.showCursor,
    required this.highlighter,
    required this.theme,
    required this.lineHeight,
    required this.padding,
    required this.lineNumberWidth,
    this.selectionStart,
    this.selectionEnd,
    this.showScrollbar = true,
  });

  // —— 行级 TextPainter 缓存（避免重复计算） —— //
  static final Map<int, Map<int, _LineCacheEntry>> _cacheByBuffer = {};

  Map<int, _LineCacheEntry> _lineCacheFor(TextBuffer buf) =>
      _cacheByBuffer.putIfAbsent(identityHashCode(buf), () => <int, _LineCacheEntry>{});

  @override
  void paint(Canvas canvas, Size size) {
    final lines = buffer.getLines();

    // 文本与行号样式
    final lineStyle = theme.base;
    final lineNumStyle = theme.base.copyWith(
      fontSize: (theme.base.fontSize ?? 16) * 0.8,
      color: Colors.grey,
    );

    // 绘制整体背景
    canvas.drawRect(Offset.zero & size, Paint()..color = Colors.white);

    // 绘制行号栏背景
    canvas.drawRect(
      Rect.fromLTWH(0, 0, lineNumberWidth, size.height),
      Paint()..color = const Color(0xFFF6F6F6),
    );

    if (lines.isEmpty) {
      _paintScrollbarIfNeeded(canvas, size, linesLength: 0);
      return;
    }

    // 计算可视区行范围
    final firstRow = ((scrollY - padding) / lineHeight).floor().clamp(0, lines.length - 1);
    final lastRow = ((scrollY + size.height - padding) / lineHeight).ceil().clamp(0, lines.length - 1);

    // 计算每行的绝对索引前缀和
    final prefix = List<int>.filled(lines.length + 1, 0);
    for (int i = 0; i < lines.length; i++) {
      prefix[i + 1] = prefix[i] + lines[i].length + 1; // +1 代表换行符
    }

    // 更新语法高亮缓存
    highlighter.rebuildIfNeeded(buffer);

    // 缓存 TextPainter
    final lineCache = _lineCacheFor(buffer);
    for (int row = firstRow; row <= lastRow; row++) {
      final text = lines[row];
      final entry = lineCache[row];
      if (entry == null || entry.text != text) {
        final spans = highlighter.buildSpansForLine(row, text);
        final tp = TextPainter(
          text: TextSpan(children: spans, style: lineStyle),
          textDirection: ui.TextDirection.ltr,
          maxLines: 1,
        )..layout();
        lineCache[row] = _LineCacheEntry(text, tp);
      }
    }

    // ===== PASS 1: 绘制选区背景 =====
    int? selA = selectionStart;
    int? selB = selectionEnd;
    if (selA != null && selB != null) {
      if (selA > selB) {
        final t = selA;
        selA = selB;
        selB = t;
      }
      for (int row = firstRow; row <= lastRow; row++) {
        final line = lines[row];
        final lineStart = prefix[row];
        final lineEnd = lineStart + line.length;

        if (selA < lineEnd && selB > lineStart) {
          final startCol = (selA - lineStart).clamp(0, line.length);
          final endCol = (selB - lineStart).clamp(0, line.length);
          final tp = lineCache[row]!.tp;

          // 获取实际字符框位置
          final boxes = tp.getBoxesForSelection(
            TextSelection(baseOffset: startCol, extentOffset: endCol),
          );

          final lineTop = padding + row * lineHeight - scrollY;
          final vAlign = (lineHeight - tp.height) / 2;
          final baseX = lineNumberWidth + padding;
          final selPaint = Paint()..color = const Color(0xFFCCE8FF);

          for (final box in boxes) {
            final rect = Rect.fromLTWH(
              baseX + box.left,
              lineTop + vAlign + box.top,
              box.right - box.left,
              box.bottom - box.top,
            );
            canvas.drawRect(rect, selPaint);
          }
        }
      }
    }

    // ===== PASS 2: 绘制文本、行号、光标 =====
    for (int row = firstRow; row <= lastRow; row++) {
      final line = lines[row];
      final absIndex = prefix[row];
      final tp = lineCache[row]!.tp;
      final lineTop = padding + row * lineHeight - scrollY;
      final vAlign = (lineHeight - tp.height) / 2;
      final baseX = lineNumberWidth + padding;

      // 行号（当前行用蓝色高亮）
      final tpNum = TextPainter(
        text: TextSpan(
          text: (row + 1).toString(),
          style: lineNumStyle.copyWith(
            color: (absIndex <= cursorPos && cursorPos <= absIndex + line.length)
                ? Colors.blue
                : Colors.grey,
          ),
        ),
        textDirection: ui.TextDirection.ltr,
        textAlign: TextAlign.right,
      )..layout(minWidth: 0, maxWidth: lineNumberWidth - 8);

      tpNum.paint(
        canvas,
        Offset(
          lineNumberWidth - 8 - tpNum.width,
          lineTop + (lineHeight - tpNum.height) / 2,
        ),
      );

      // 文本内容
      tp.paint(canvas, Offset(baseX, lineTop + vAlign));

      // 光标绘制
      if (showCursor && cursorPos >= absIndex && cursorPos <= absIndex + line.length) {
        final col = cursorPos - absIndex;
        final caretOffset = tp.getOffsetForCaret(TextPosition(offset: col), Rect.zero);

        // 像素对齐，避免模糊
        double snap(double x) => x.floorToDouble() + 0.5;

        final caretX = snap(baseX + caretOffset.dx);
        final caretTop = lineTop + kCaretPadding;
        final caretHeight = lineHeight - kCaretPadding * 2;

        final r = RRect.fromRectAndRadius(
          Rect.fromLTWH(caretX, caretTop, kCaretWidth, caretHeight),
          kCaretRadius,
        );
        canvas.drawRRect(r, Paint()..color = Colors.blue);
      }
    }

    // ===== PASS 3: 绘制右侧滚动条 =====
    _paintScrollbarIfNeeded(canvas, size, linesLength: lines.length);
  }

  /// 绘制右侧滚动条（只读，非交互）。
  void _paintScrollbarIfNeeded(Canvas canvas, Size size, {required int linesLength}) {
    if (!showScrollbar) return;

    final contentHeight = padding * 2 + linesLength * lineHeight;
    final maxScroll = (contentHeight - size.height);
    if (maxScroll <= 0) return; // 内容没超出，不需要滚动条

    final trackX = size.width - kScrollbarMargin - kScrollbarWidth;
    final trackY = kScrollbarMargin;
    final trackH = size.height - kScrollbarMargin * 2;

    // thumb 高度按「可视比例」计算，最小限制
    final thumbH = (size.height * size.height / contentHeight)
        .clamp(kScrollbarMinThumb, trackH);

    final clampedScroll = scrollY.clamp(0.0, maxScroll);
    final thumbY = trackY + (clampedScroll / maxScroll) * (trackH - thumbH);

    // 绘制轨道
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromLTWH(trackX, trackY, kScrollbarWidth, trackH),
        kScrollbarRadius,
      ),
      Paint()..color = Colors.black12,
    );

    // 绘制滑块
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromLTWH(trackX, thumbY, kScrollbarWidth, thumbH),
        kScrollbarRadius,
      ),
      Paint()..color = Colors.black45,
    );
  }

  @override
  bool shouldRepaint(LineNumberEditorPainter old) =>
      buffer.version != old.buffer.version ||
          cursorPos != old.cursorPos ||
          showCursor != old.showCursor ||
          selectionStart != old.selectionStart ||
          selectionEnd != old.selectionEnd ||
          scrollY != old.scrollY ||
          lineHeight != old.lineHeight ||
          padding != old.padding ||
          lineNumberWidth != old.lineNumberWidth ||
          highlighter.version != old.highlighter.version ||
          theme != old.theme ||
          showScrollbar != old.showScrollbar;
}

/// 行缓存条目：避免重复构造 TextPainter。
class _LineCacheEntry {
  final String text;
  final TextPainter tp;
  _LineCacheEntry(this.text, this.tp);
}
